#define JMBC_INSIDE
#include "mlModel.h"
#include <Math/mlMath.h>
#include <Core/clCore.h>
#include <Material/mlMaterial.h>
#include <stdio.h>
#include <string.h>
#include <GL/gl.h>
//#include <ode/ode.h>

#define mdlg(g) model->groups[ML_MODEL_GROUP_ ## g]
#define mdls(t) sizeof(MLModel ## t)

// Context

#define STORE ID_SMALL_STORE

static MLModel *store[STORE];

void
mlModelCreateContext(void)
{
	idClearStore(MLModel, STORE, store);
}

// Generation / Deletion

static
void
Init(MLModel *model)
{
	model->action = model->frame = 0;
}

static
void
Term(MLModel *model)
{
}

void
mlGenModels(unsigned int n, unsigned int *models)
{
	idGenObjects(MLModel, STORE, store, Init, n, models);
}

void
mlDeleteModels(unsigned int n, unsigned int *models)
{
	idDeleteObjects(store, Term, n, models);
}

// Load / Unload

void
mlModelLoad(unsigned int id, unsigned int filepath)
{
	FILE *stream;
	char tmp[256];

	//stream = _wfopen(tlStringData(filepath), L"rb");
	wcstombs(tmp, tlStringData(filepath), 256);
	stream = fopen(tmp, "rb");

	if(stream)
	{
		MLModel *model;
		MLModelHeader header;

		model = store[id];

		fread(&header, 1, mdls(Header), stream);

		tlGenGroups(10, model->groups);

		tlGroupRead(mdlg(BONES), header.bones, mdls(Bone), stream);
		tlGroupRead(mdlg(PARENTS), header.parents, mdls(Parent), stream);
		tlGroupRead(mdlg(VERTICES), header.vertices, mdls(Vertex), stream);
		tlGroupRead(mdlg(MATERIALS), header.materials, mdls(Material), stream);
		tlGroupRead(mdlg(FACES), header.faces, mdls(Face), stream);
		tlGroupRead(mdlg(TAGS), header.tags, mdls(Tag), stream);
		tlGroupRead(mdlg(WEIGHTS), header.weights, mdls(Weight), stream);
		tlGroupRead(mdlg(FRAMES), header.frames, mdls(Frame), stream);
		tlGroupRead(mdlg(ACTIONS), header.actions, mdls(Action), stream);
		tlGroupAllocate(mdlg(ANIM), header.vertices, sizeof(float) * 3);

		fclose(stream);
	}
	else
		elErrorSet(EL_ERROR_INVALID);
}

void
mlModelUnload(unsigned int id)
{
	MLModel *model;

	model = store[id];

	tlDeleteGroups(10, model->groups);
}

void
mlModelsUnload(unsigned int n, unsigned int *models)
{
	unsigned int i;

	for(i = 0; i < n; i++)
		mlModelUnload(models[i]);
}

// Operation

void
mlModelCompile(unsigned int id, unsigned int filepath)
{
	FILE *stream;
	char tmp[256];

	//stream = _wfopen(tlStringData(filepath), L"r");
	wcstombs(tmp, tlStringData(filepath), 256);
	stream = fopen(tmp, "r");

	if(stream)
	{
		MLModel *model;
		wchar_t buffer[512];
		MLModelHeader header;
		MLModelBone *bones;
		MLModelParent *parents;
		MLModelVertex *vertices;
		MLModelMaterial *materials;
		MLModelFace *faces;
		MLModelTag *tags;
		MLModelWeight *weights;
		MLModelFrame *frames;
		MLModelAction *actions;
		unsigned int i;

		model = store[id];

		tlGenGroups(10, model->groups);

		fgetws(buffer, 512, stream);
		swscanf(buffer, L"bones %i", &header.bones);

		wprintf(L"Bones: %i\n", header.bones);

		tlGroupAllocate(mdlg(BONES), header.bones, mdls(Bone));
		bones = tlGroupArray(mdlg(BONES));

		for(i = 0; i < header.bones; i++)
		{
			fgetws(buffer, 512, stream);
			swscanf(buffer, L"start %i", &bones[i].start);

			fgetws(buffer, 512, stream);
			swscanf(buffer, L"parents %i", &bones[i].parents);
		}

		fgetws(buffer, 512, stream);
		swscanf(buffer, L"parents %i", &header.parents);

		wprintf(L"Parents: %i\n", header.parents);

		tlGroupAllocate(mdlg(PARENTS), header.parents, mdls(Parent));
		parents = tlGroupArray(mdlg(PARENTS));

		for(i = 0; i < header.parents; i++)
		{
			fgetws(buffer, 512, stream);
			swscanf(buffer, L"bone %i", &parents[i].bone);
		}

		fgetws(buffer, 512, stream);
		swscanf(buffer, L"vertices %i", &header.vertices);

		wprintf(L"Vertices: %i\n", header.vertices);

		tlGroupAllocate(mdlg(VERTICES), header.vertices, mdls(Vertex));
		vertices = tlGroupArray(mdlg(VERTICES));

		for(i = 0; i < header.vertices; i++)
		{
			fgetws(buffer, 512, stream);
			swscanf(buffer, L"coord %f %f %f", &vertices[i].coord[0], &vertices[i].coord[1], &vertices[i].coord[2]);

			fgetws(buffer, 512, stream);
			swscanf(buffer, L"normal %f %f %f", &vertices[i].normal[0], &vertices[i].normal[1], &vertices[i].normal[2]);

			fgetws(buffer, 512, stream);
			swscanf(buffer, L"start %i", &vertices[i].start);

			fgetws(buffer, 512, stream);
			swscanf(buffer, L"weights %i", &vertices[i].weights);
		}

		fgetws(buffer, 512, stream);
		swscanf(buffer, L"materials %i", &header.materials);

		wprintf(L"Materials: %i\n", header.materials);

		tlGroupAllocate(mdlg(MATERIALS), header.materials, mdls(Material));
		materials = tlGroupArray(mdlg(MATERIALS));

		for(i = 0; i < header.materials; i++)
		{
			fgetws(buffer, 512, stream);
			memset(materials[i].name, 0, sizeof(wchar_t) * TL_STRING_DEFAULT_FIXED_SIZE);
			wcsncpy(materials[i].name, buffer + wcslen(L"name "), wcslen(buffer) - wcslen(L"name ") - 1);

			fgetws(buffer, 512, stream);
			swscanf(buffer, L"start %i", &materials[i].start);

			fgetws(buffer, 512, stream);
			swscanf(buffer, L"faces %i", &materials[i].faces);
		}

		fgetws(buffer, 512, stream);
		swscanf(buffer, L"faces %i", &header.faces);

		wprintf(L"Faces: %i\n", header.faces);

		tlGroupAllocate(mdlg(FACES), header.faces, mdls(Face));
		faces = tlGroupArray(mdlg(FACES));

		for(i = 0; i < header.faces; i++)
		{
			fgetws(buffer, 512, stream);
			swscanf(buffer, L"vertices %i %i %i", &faces[i].vertices[0], &faces[i].vertices[1], &faces[i].vertices[2]);

			fgetws(buffer, 512, stream);
			swscanf(buffer, L"normal %f %f %f", &faces[i].normal[0], &faces[i].normal[1], &faces[i].normal[2]);

			fgetws(buffer, 512, stream);
			swscanf(buffer, L"texcoords %f %f %f %f %f %f", &faces[i].texcoords[0][0], &faces[i].texcoords[0][1], &faces[i].texcoords[1][0], &faces[i].texcoords[1][1], &faces[i].texcoords[2][0], &faces[i].texcoords[2][1]);
		}

		fgetws(buffer, 512, stream);
		swscanf(buffer, L"tags %i", &header.tags);

		wprintf(L"Tags: %i\n", header.tags);

		tlGroupAllocate(mdlg(TAGS), header.tags, mdls(Tag));
		tags = tlGroupArray(mdlg(TAGS));

		for(i = 0; i < header.tags; i++)
		{
			fgetws(buffer, 512, stream);
			memset(tags[i].name, 0, sizeof(wchar_t) * TL_STRING_DEFAULT_FIXED_SIZE);
			wcsncpy(tags[i].name, buffer + wcslen(L"name "), wcslen(buffer) - wcslen(L"name ") - 1);
			tags[i].name[wcslen(buffer) - wcslen(L"name ") - 1] = L'\0';

			fgetws(buffer, 512, stream);
			swscanf(buffer, L"matrix %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f", &tags[i].matrix[0], &tags[i].matrix[1], &tags[i].matrix[2], &tags[i].matrix[3], &tags[i].matrix[4], &tags[i].matrix[5], &tags[i].matrix[6], &tags[i].matrix[7], &tags[i].matrix[8], &tags[i].matrix[9], &tags[i].matrix[10], &tags[i].matrix[11], &tags[i].matrix[12], &tags[i].matrix[13], &tags[i].matrix[14], &tags[i].matrix[15]);

			fgetws(buffer, 512, stream);
			swscanf(buffer, L"start %i", &tags[i].start);

			fgetws(buffer, 512, stream);
			swscanf(buffer, L"weights %i", &tags[i].weights);
		}

		fgetws(buffer, 512, stream);
		swscanf(buffer, L"weights %i", &header.weights);

		wprintf(L"Weights: %i\n", header.weights);

		tlGroupAllocate(mdlg(WEIGHTS), header.weights, mdls(Weight));
		weights = tlGroupArray(mdlg(WEIGHTS));

		for(i = 0; i < header.weights; i++)
		{
			fgetws(buffer, 512, stream);
			swscanf(buffer, L"weight %i %f", &weights[i].bone, &weights[i].value);
		}

		fgetws(buffer, 512, stream);
		swscanf(buffer, L"frames %i", &header.frames);

		wprintf(L"Frames: %i\n", header.frames / header.bones);

		tlGroupAllocate(mdlg(FRAMES), header.frames, mdls(Frame));
		frames = tlGroupArray(mdlg(FRAMES));

		for(i = 0; i < header.frames; i++)
		{
			fgetws(buffer, 512, stream);
			swscanf(buffer, L"matrix %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f", &frames[i].matrix[0], &frames[i].matrix[1], &frames[i].matrix[2], &frames[i].matrix[3], &frames[i].matrix[4], &frames[i].matrix[5], &frames[i].matrix[6], &frames[i].matrix[7], &frames[i].matrix[8], &frames[i].matrix[9], &frames[i].matrix[10], &frames[i].matrix[11], &frames[i].matrix[12], &frames[i].matrix[13], &frames[i].matrix[14], &frames[i].matrix[15]);
		}

		fgetws(buffer, 512, stream);
		swscanf(buffer, L"actions %i", &header.actions);

		wprintf(L"Actions: %i\n", header.actions);

		tlGroupAllocate(mdlg(ACTIONS), header.actions, mdls(Action));
		actions = tlGroupArray(mdlg(ACTIONS));

		for(i = 0; i < header.actions; i++)
		{
			fgetws(buffer, 512, stream);
			memset(actions[i].name, 0, sizeof(wchar_t) * TL_STRING_DEFAULT_FIXED_SIZE);
			wcsncpy(actions[i].name, buffer + wcslen(L"name "), wcslen(buffer) - wcslen(L"name ") - 1);
			actions[i].name[wcslen(buffer) - wcslen(L"name ") - 1] = L'\0';

			fgetws(buffer, 512, stream);
			swscanf(buffer, L"start %i", &actions[i].start);

			fgetws(buffer, 512, stream);
			swscanf(buffer, L"frames %i", &actions[i].frames);
		}

		tlGroupAllocate(mdlg(ANIM), header.vertices, sizeof(float) * 3);

		fclose(stream);
	}
	else
		elErrorSet(EL_ERROR_INVALID);
}

void
mlModelDecompile(unsigned int id, unsigned int filepath)
{
	FILE *stream;
	char tmp[256];

	//stream = _wfopen(tlStringData(filepath), L"w");
	wcstombs(tmp, tlStringData(filepath), 256);
	stream = fopen(tmp, "w");

	if(stream)
	{
		MLModel *model;
		MLModelHeader header;
		MLModelBone *bones;
		MLModelParent *parents;
		MLModelVertex *vertices;
		MLModelMaterial *materials;
		MLModelFace *faces;
		MLModelTag *tags;
		MLModelWeight *weights;
		MLModelFrame *frames;
		MLModelAction *actions;
		unsigned int i;

		model = store[id];

		header.bones = tlGroupLength(mdlg(BONES));
		bones = tlGroupArray(mdlg(BONES));

		fwprintf(stream, L"bones %i\n", header.bones);

		for(i = 0; i < header.bones; i++)
		{
			fwprintf(stream, L"start %i\n", bones[i].start);

			fwprintf(stream, L"parents %i\n", bones[i].parents);
		}

		header.parents = tlGroupLength(mdlg(PARENTS));
		parents = tlGroupArray(mdlg(PARENTS));

		fwprintf(stream, L"parents %i\n", header.parents);

		for(i = 0; i < header.parents; i++)
		{
			fwprintf(stream, L"bone %i\n", parents[i].bone);
		}

		header.vertices = tlGroupLength(mdlg(VERTICES));
		vertices = tlGroupArray(mdlg(VERTICES));

		fwprintf(stream, L"vertices %i\n", header.vertices);

		for(i = 0; i < header.vertices; i++)
		{
			fwprintf(stream, L"coord %f %f %f\n", vertices[i].coord[0], vertices[i].coord[1], vertices[i].coord[2]);

			fwprintf(stream, L"normal %f %f %f\n", vertices[i].normal[0], vertices[i].normal[1], vertices[i].normal[2]);

			fwprintf(stream, L"start %i\n", vertices[i].start);

			fwprintf(stream, L"weights %i\n", vertices[i].weights);
		}

		header.materials = tlGroupLength(mdlg(MATERIALS));
		materials = tlGroupArray(mdlg(MATERIALS));

		fwprintf(stream, L"materials %i\n", header.materials);

		for(i = 0; i < header.materials; i++)
		{
			fwprintf(stream, L"name %s\n", materials[i].name);

			fwprintf(stream, L"start %i\n", materials[i].start);

			fwprintf(stream, L"faces %i\n", materials[i].faces);
		}

		header.faces = tlGroupLength(mdlg(FACES));
		faces = tlGroupArray(mdlg(FACES));

		fwprintf(stream, L"faces %i\n", header.faces);

		for(i = 0; i < header.faces; i++)
		{
			fwprintf(stream, L"vertices %i %i %i\n", faces[i].vertices[0], faces[i].vertices[1], faces[i].vertices[2]);

			fwprintf(stream, L"normal %f %f %f\n", faces[i].normal[0], faces[i].normal[1], faces[i].normal[2]);

			fwprintf(stream, L"texcoords %f %f %f %f %f %f\n", faces[i].texcoords[0][0], faces[i].texcoords[0][1], faces[i].texcoords[1][0], faces[i].texcoords[1][1], faces[i].texcoords[2][0], faces[i].texcoords[2][1]);
		}

		header.tags = tlGroupLength(mdlg(TAGS));
		tags = tlGroupArray(mdlg(TAGS));

		fwprintf(stream, L"tags %i\n", header.tags);

		for(i = 0; i < header.tags; i++)
		{
			fwprintf(stream, L"name %s\n", tags[i].name);

			fwprintf(stream, L"matrix %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f\n", tags[i].matrix[0], tags[i].matrix[1], tags[i].matrix[2], tags[i].matrix[3], tags[i].matrix[4], tags[i].matrix[5], tags[i].matrix[6], tags[i].matrix[7], tags[i].matrix[8], tags[i].matrix[9], tags[i].matrix[10], tags[i].matrix[11], tags[i].matrix[12], tags[i].matrix[13], tags[i].matrix[14], tags[i].matrix[15]);

			fwprintf(stream, L"start %i\n", tags[i].start);

			fwprintf(stream, L"weights %i\n", tags[i].weights);
		}

		header.weights = tlGroupLength(mdlg(WEIGHTS));
		weights = tlGroupArray(mdlg(WEIGHTS));

		fwprintf(stream, L"weights %i\n", header.weights);

		for(i = 0; i < header.weights; i++)
		{
			fwprintf(stream, L"weight %i %f\n", weights[i].bone, weights[i].value);
		}

		header.frames = tlGroupLength(mdlg(FRAMES));
		frames = tlGroupArray(mdlg(FRAMES));

		fwprintf(stream, L"frames %i\n", header.frames);

		for(i = 0; i < header.frames; i++)
		{
			fwprintf(stream, L"matrix %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f\n", frames[i].matrix[0], frames[i].matrix[1], frames[i].matrix[2], frames[i].matrix[3], frames[i].matrix[4], frames[i].matrix[5], frames[i].matrix[6], frames[i].matrix[7], frames[i].matrix[8], frames[i].matrix[9], frames[i].matrix[10], frames[i].matrix[11], frames[i].matrix[12], frames[i].matrix[13], frames[i].matrix[14], frames[i].matrix[15]);
		}

		header.actions = tlGroupLength(mdlg(ACTIONS));
		actions = tlGroupArray(mdlg(ACTIONS));

		fwprintf(stream, L"actions %i\n", header.actions);

		for(i = 0; i < header.actions; i++)
		{
			fwprintf(stream, L"name %s\n", actions[i].name);

			fwprintf(stream, L"start %i\n", actions[i].start);

			fwprintf(stream, L"frames %i\n", actions[i].frames);
		}

		fclose(stream);
	}
	else
		elErrorSet(EL_ERROR_INVALID);
}

unsigned int
mlModelBones(unsigned int id)
{
	MLModel *model;

	model = store[id];

	return tlGroupLength(mdlg(BONES));
}

unsigned int
mlModelParents(unsigned int id)
{
	MLModel *model;

	model = store[id];

	return tlGroupLength(mdlg(PARENTS));
}

unsigned int
mlModelVertices(unsigned int id)
{
	MLModel *model;

	model = store[id];

	return tlGroupLength(mdlg(VERTICES));
}

unsigned int
mlModelMaterials(unsigned int id)
{
	MLModel *model;

	model = store[id];

	return tlGroupLength(mdlg(MATERIALS));
}

unsigned int
mlModelMaterial(unsigned int id, unsigned int name)
{
	MLModel *model;
	MLModelMaterial *materials;
	unsigned int i;

	model = store[id];

	materials = tlGroupArray(mdlg(MATERIALS));
	for(i = 0; wcscmp(materials[i].name, tlStringData(name)); i++);

	return i;
}

unsigned int
mlModelMaterialMaterial(unsigned int id, unsigned int material)
{
	MLModel *model;

	model = store[id];

	return ((unsigned int *) tlGroupArray(model->materials))[material];
}

unsigned int
mlModelFaces(unsigned int id)
{
	MLModel *model;

	model = store[id];

	return tlGroupLength(mdlg(FACES));
}

unsigned int
mlModelTags(unsigned int id)
{
	MLModel *model;

	model = store[id];

	return tlGroupLength(mdlg(TAGS));
}

unsigned int
mlModelTag(unsigned int id, unsigned int name)
{
	MLModel *model;
	MLModelTag *tags;
	unsigned int i;

	model = store[id];

	tags = tlGroupArray(mdlg(TAGS));
	for(i = 0; wcscmp(tags[i].name, tlStringData(name)); i++);

	return i;
}

void
mlModelTagMatrix(unsigned int id, unsigned int tag, float matrix[16])
{
	MLModel *model;
	MLModelTag *tags;
	MLModelWeight *weights;
	MLModelFrame *frames;
	MLModelAction *actions;
	float tmp[16];
	unsigned int i;

	model = store[id];

	tags = tlGroupArray(mdlg(TAGS));
	weights = tlGroupArray(mdlg(WEIGHTS));
	frames = tlGroupArray(mdlg(FRAMES));
	actions = tlGroupArray(mdlg(ACTIONS));

	mlMatrixLoadZero(tmp);
	for(i = tags[tag].start; i < tags[tag].start + tags[tag].weights; i++)
	{
		mlMatrixMultScalarTo(frames[(actions[model->action].start + model->frame) * tlGroupLength(mdlg(BONES)) + weights[i].bone].matrix, weights[i].value, matrix);
		mlMatrixAdd(tmp, matrix);
	}
	mlMatrixMultTo(tmp, tags[tag].matrix, matrix);
}

unsigned int
mlModelWeights(unsigned int id)
{
	MLModel *model;

	model = store[id];

	return tlGroupLength(mdlg(WEIGHTS));
}

unsigned int
mlModelFrames(unsigned int id)
{
	MLModel *model;

	model = store[id];

	return tlGroupLength(mdlg(FRAMES));
}

unsigned int
mlModelActions(unsigned int id)
{
	MLModel *model;

	model = store[id];

	return tlGroupLength(mdlg(ACTIONS));
}

unsigned int
mlModelAction(unsigned int id, unsigned int name)
{
	MLModel *model;
	MLModelAction *actions;
	unsigned int i;

	model = store[id];

	actions = tlGroupArray(mdlg(ACTIONS));
	for(i = 0; wcscmp(actions[i].name, tlStringData(name)); i++);

	return i;
}

unsigned int
mlModelActionFrames(unsigned int id, unsigned int action)
{
	MLModel *model;
	MLModelAction *actions;

	model = store[id];

	actions = tlGroupArray(mdlg(ACTIONS));

	return actions[action].frames;
}

void
mlModelActionSet(unsigned int id, unsigned int action)
{
	MLModel *model;

	model = store[id];

	model->action = action;
}

void
mlModelActionFrameSet(unsigned int id, unsigned int frame)
{
	MLModel *model;

	model = store[id];

	model->frame = frame;
}

float *
mlModelMatrix(unsigned int id)
{
	MLModel *model;

	model = store[id];

	return model->matrix;
}

// Disk I/O

void
mlModelSave(unsigned int id, unsigned int filepath)
{
	FILE *stream;
	char tmp[256];

	//stream = _wfopen(tlStringData(filepath), L"wb");
	wcstombs(tmp, tlStringData(filepath), 256);
	stream = fopen(tmp, "wb");

	if(stream)
	{
		MLModel *model;
		MLModelHeader header;

		model = store[id];

		header.bones = tlGroupLength(mdlg(BONES));
		header.parents = tlGroupLength(mdlg(PARENTS));
		header.vertices = tlGroupLength(mdlg(VERTICES));
		header.materials = tlGroupLength(mdlg(MATERIALS));
		header.faces = tlGroupLength(mdlg(FACES));
		header.tags = tlGroupLength(mdlg(TAGS));
		header.weights = tlGroupLength(mdlg(WEIGHTS));
		header.frames = tlGroupLength(mdlg(FRAMES));
		header.actions = tlGroupLength(mdlg(ACTIONS));

		fwrite(&header, 1, mdls(Header), stream);

		tlGroupWrite(mdlg(BONES), stream);
		tlGroupWrite(mdlg(PARENTS), stream);
		tlGroupWrite(mdlg(VERTICES), stream);
		tlGroupWrite(mdlg(MATERIALS), stream);
		tlGroupWrite(mdlg(FACES), stream);
		tlGroupWrite(mdlg(TAGS), stream);
		tlGroupWrite(mdlg(WEIGHTS), stream);
		tlGroupWrite(mdlg(FRAMES), stream);
		tlGroupWrite(mdlg(ACTIONS), stream);

		fclose(stream);
	}
	else
		elErrorSet(EL_ERROR_INVALID);
}

void
mlModelGenMaterials(unsigned int id, unsigned int path)
{
	MLModel *model;
	MLModelMaterial *src;
	unsigned int *dst;
	unsigned int strings[5];
	unsigned int elError;
	unsigned int i;

	model = store[id];

	src = tlGroupArray(mdlg(MATERIALS));
	tlGenGroups(1, &model->materials);
	tlGroupAllocate(model->materials, tlGroupLength(mdlg(MATERIALS)), sizeof(unsigned int));
	dst = tlGroupArray(model->materials);
	mlGenMaterials(tlGroupLength(mdlg(MATERIALS)), dst);

	tlGenStrings(4, &strings[1]);
	strings[0] = path;

	for(i = 0; i < tlGroupLength(mdlg(MATERIALS)); i++)
	{
		tlStringLoad(strings[1], src[i].name);
		tlStringConcatPath(strings[2], 2, strings);
		tlStringLoad(strings[3], L".cmaterial");
		tlStringConcat(strings[4], 2, &strings[2]);
		mlMaterialLoad(dst[i], strings[4]);
		elError = elErrorGet();
		tlStringsUnload(2, &strings[3]);
		if(elError != EL_ERROR_NONE)
		{
			elErrorSet(EL_ERROR_NONE);
			tlStringLoad(strings[3], L".material");
			tlStringConcat(strings[4], 2, &strings[2]);
			mlMaterialCompile(dst[i], strings[4]);
			tlStringsUnload(2, &strings[3]);
		}
		tlStringsUnload(2, &strings[1]);
		mlMaterialGenTextures(dst[i], strings[0]);
	}

	tlDeleteStrings(4, &strings[1]);
}

void
mlModelDeleteMaterials(unsigned int id)
{
	MLModel *model;
	unsigned int i;
	unsigned int *materials;

	model = store[id];

	materials = tlGroupArray(model->materials);
	for(i = 0; i < tlGroupLength(model->materials); i++)
		mlMaterialDeleteTextures(materials[i]);
	mlMaterialsUnload(tlGroupLength(model->materials), tlGroupArray(model->materials));
	tlGroupUnload(model->materials);
	tlDeleteGroups(1, &model->materials);
}

// OpenGL Integration

void
mlModelDraw(unsigned int id)
{
	MLModel *model;
	MLModelVertex *vertices;
	MLModelMaterial *materials;
	MLModelFace *faces;
	MLModelWeight *weights;
	MLModelFrame *frames;
	MLModelAction *actions;
	float (*coords)[3];
	unsigned int *material;
	float matrix[16], tmp[3];
	unsigned int i, j, k;

	model = store[id];

	vertices = tlGroupArray(mdlg(VERTICES));
	materials = tlGroupArray(mdlg(MATERIALS));
	faces = tlGroupArray(mdlg(FACES));
	weights = tlGroupArray(mdlg(WEIGHTS));
	frames = tlGroupArray(mdlg(FRAMES));
	actions = tlGroupArray(mdlg(ACTIONS));
	coords = tlGroupArray(mdlg(ANIM));

	material = tlGroupArray(model->materials);

	for(i = 0; i < tlGroupLength(mdlg(VERTICES)); i++)
	{
		mlPointLoadZero(coords[i]);
		for(j = vertices[i].start; j < vertices[i].start + vertices[i].weights; j++)
		{
			mlMatrixMultScalarTo(frames[(actions[model->action].start + model->frame) * tlGroupLength(mdlg(BONES)) + weights[j].bone].matrix, weights[j].value, matrix);
			mlMatrixMultVectorTo(matrix, vertices[i].coord, tmp);
			mlPointAdd(coords[i], tmp);
		}
	}

	for(i = 0; i < tlGroupLength(mdlg(MATERIALS)); i++)
	{
		mlMaterialBind(material[i]);

		glMatrixMode(GL_TEXTURE);
		glPushMatrix();
		glMultMatrixf(mlMaterialMatrix(material[i]));
		glMatrixMode(GL_MODELVIEW);

		glBegin(GL_TRIANGLES);
		for(j = materials[i].start; j < materials[i].start + materials[i].faces; j++)
		{
			for(k = 0; k < 3; k++)
			{
				glTexCoord2fv(faces[j].texcoords[k]);
				glNormal3fv(vertices[faces[j].vertices[k]].normal);
				glVertex3fv(coords[faces[j].vertices[k]]);
			}
		}
		glEnd();

		glMatrixMode(GL_TEXTURE);
		glPopMatrix();
		glMatrixMode(GL_MODELVIEW);
	}
}

/*
void
mlModelDrawTag(unsigned int id, unsigned int action, unsigned int frame, unsigned int tag, unsigned int submodel, unsigned int subtextures, unsigned int subaction, unsigned int subframe)
{
	MLModel *model;
	MLModelTag *tags;
	MLModelWeight *weights;
	MLModelFrame *frames;
	MLModelAction *actions;
	unsigned int i;
	float tmp[16], matrix[16];

	model = store[id];

	tags = tlGroupArray(mdlg(TAGS));
	weights = tlGroupArray(mdlg(WEIGHTS));
	frames = tlGroupArray(mdlg(FRAMES));
	actions = tlGroupArray(mdlg(ACTIONS));

	weight = tlGroupArray(weights[vertices + tag]);

	mlMatrixLoadZero(tmp);
	for(i = tags[tag].start; i < tags[tag].weights; i++)
	{
		mlMatrixMultScalarTo(frames[(actions[action].start + frame) * tlGroupLength(mdlg(BONES)) + weights[i].bone].matrix, weights[i].value, matrix);
		mlMatrixAdd(tmp, matrix);
	}
	mlMatrixMultTo(tmp, tags[tag].matrix, matrix);
	
	glPushMatrix();
	glMultMatrixf(matrix);
	//glRotatef(-90, 1.0f, 0.0f, 0.0f);
	mlModelDraw(submodel, subtextures, subaction, subframe);
	glPopMatrix();
}

void
mlModelFrustumDraw(unsigned int id, unsigned int textures, unsigned int frustum, unsigned int action, unsigned int frame)
{
	unsigned int i, j, k;
	MLModelVertex *vertices;
	MLModelWeight *weights;
	MLModelFrame *frames;
	MLModelAction *actions;
	GLuint *gltextures;
	MLModelMaterial *materials;
	MLModelFace *faces;
	float (*coords)[3];
	float matrix[16], tmp[3];
	model = store[id];
	vertices = tlGroupArray(mdlg(VERTICES));
	weights = tlGroupArray(mdlg(WEIGHTS));
	frames = tlGroupArray(mdlg(FRAMES));
	actions = tlGroupArray(mdlg(ACTIONS));
	gltextures = tlGroupArray(textures);
	materials = tlGroupArray(mdlg(MATERIALS));
	faces = tlGroupArray(mdlg(FACES));
	coords = tlGroupArray(mdlg(ANIM));
	for(i = 0; i < tlGroupLength(mdlg(VERTICES)); i++)
	{
		mlPointLoadZero(coords[i]);
		weight = tlGroupArray(weights[i]);
		for(j = 0; j < tlGroupLength(weights[i]); j++)
		{
			mlMatrixMultScalarTo(frames[(actions[action].start + frame) * header.bones + weight[j].bone].matrix, weight[j].value, matrix);
			mlMatrixMultVectorTo(matrix, vertices[i].coord, tmp);
			mlPointAdd(coords[i], tmp);
		}
	}
	for(i = 0; i < tlGroupLength(mdlg(FACES)); i++)
	{
		float triangle[9] =
		{
			coords[faces[i].vertices[0]][0],
			coords[faces[i].vertices[0]][1],
			coords[faces[i].vertices[0]][2],
			coords[faces[i].vertices[1]][0],
			coords[faces[i].vertices[1]][1],
			coords[faces[i].vertices[1]][2],
			coords[faces[i].vertices[2]][0],
			coords[faces[i].vertices[2]][1],
			coords[faces[i].vertices[2]][2],
		};
		if(mlFrustumTriangle(frustum, triangle))
		{
			float ambient[4] =
			{
				materials[faces[i].material].ambient[0],
				materials[faces[i].material].ambient[1],
				materials[faces[i].material].ambient[2],
				materials[faces[i].material].alpha
			};
			float specular[4] =
			{
				materials[faces[i].material].specular[0],
				materials[faces[i].material].specular[1],
				materials[faces[i].material].specular[2],
				materials[faces[i].material].alpha
			};
			float diffuse[4] =
			{
				materials[faces[i].material].diffuse[0],
				materials[faces[i].material].diffuse[1],
				materials[faces[i].material].diffuse[2],
				materials[faces[i].material].alpha
			};
			glBindTexture(GL_TEXTURE_2D, gltextures[materials[faces[i].material].texture]);
			glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, ambient);
			glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse);
			glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular);
			glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, ambient);
			glBegin(GL_TRIANGLES);
			for(j = 0; j < 3; j++)
			{
				glTexCoord2fv(faces[i].texcoords[j]);
				glNormal3fv(vertices[faces[i].vertices[j]].normal);
				glVertex3fv(coords[faces[i].vertices[j]]);
			}
			glEnd();
		}
	}
}
*/
